
open Namespace_ast

module StrSet = Set.Make(String)

type absolute = {
	abs_idents : StrSet.t
}

let absolute_list func (data:absolute) (declared:StrSet.t) lst = 
	List.fold_left (fun data e -> func data declared e) data lst

let rec absolute_sourceElement data declared = function
	| AStatement(s) -> absolute_statement data declared s
	| ANamespace stmt -> absolute_namespace data declared stmt
	(*| AFunctionDeclaration(funcDecl) -> absolute_functionDeclaration data declared funcDecl *) (* Modif #1 *)

and absolute_statement data declared = function
	| ABlock(block) -> absolute_block data declared block
	| AVariableStatement(varStmt) -> absolute_variableStatement data declared varStmt
	| AClassStatement(classStmt) -> absolute_classStatement data declared classStmt
	| AOperatorAssignment stmt -> absolute_operatorAssignment data declared stmt
	| AOperatorUnassignment _ -> data
	| AConstStatement(varStmt) -> absolute_variableStatement data declared varStmt		(* Modif #2 *)
	| AFuncDeclaration(funcDecl) -> absolute_functionDeclaration data declared funcDecl	(* Modif #1 *)
	| AEmptyStatement -> data
	| AExpressionStatement(expressionStatement) -> absolute_expressionStatement data declared expressionStatement
	| AIfStatement(ifStatement) -> absolute_ifStatement data declared ifStatement
	| AIterationStatement(iterationStatement) -> absolute_iterationStatement data declared iterationStatement
	| AContinueStatement _ -> data
	| ABreakStatement _ -> data
	| AReturnStatement(returnStatement) -> absolute_returnStatement data declared returnStatement
	| AWithStatement(withStatement) -> absolute_withStatement data declared withStatement
	| ALabelledStatement(labelledStatement) -> absolute_labelledStatement data declared labelledStatement
	| ASwitchStatement(switchStatement) -> absolute_switchStatement data declared switchStatement
	| AThrowStatement(throwStatement) -> absolute_throwStatement data declared throwStatement
	| ATryStatement(tryStatement) -> absolute_tryStatement data declared tryStatement
	| ADebuggerStatement -> data

and absolute_namespace data declared (idents,body) =
	let data = absolute_body data declared body in
	(match idents with
		| [] -> data
		| a::_ -> {
			abs_idents = StrSet.add a data.abs_idents
		})

and absolute_operatorAssignment data declared (_,expr) =
	absolute_assignmentExpression data declared expr

and absolute_classStatement data declared (_,_,ident,extends,classElements) =
	let data = if StrSet.mem ident declared then data else {
		abs_idents = StrSet.add ident data.abs_idents
	} in
	let data = match extends with
		| None -> data
		| Some extends -> (match extends with
			| [] -> data
			| ident::_ -> if StrSet.mem ident declared then data else {
					abs_idents = StrSet.add ident data.abs_idents
				}
			)
	in
	absolute_list absolute_classElement data declared classElements

and absolute_classElement data declared = function
	| ACVariableStatement vs -> absolute_variableStatement data declared vs
	| ACConstStatement cs -> absolute_variableStatement data declared cs
	| ACFuncDeclaration funcDecl -> absolute_functionDeclaration data declared funcDecl

and absolute_block data declared block =
	absolute_list absolute_statement data declared block

and absolute_variableStatement data declared (_,_,stmt) =
	absolute_variableDeclarationList data declared stmt

and absolute_variableDeclarationList data declared decls =
	absolute_list absolute_variableDeclaration data declared decls;

and absolute_variableDeclaration data declared (ident,init) = match init with
	| None -> data
	| Some init -> absolute_initializer data declared init

and absolute_initializer data declared assignExpr =
	absolute_assignmentExpression data declared assignExpr

and absolute_assignmentExpression data declared = function
	| ACondExpr(condExpr) -> absolute_conditionalExpression data declared condExpr
	| AAssignOp(lhs, op, assignExpr) ->
		let data = absolute_leftHandSideExpression data declared lhs in
		absolute_assignmentExpression data declared assignExpr

and absolute_leftHandSideExpression data declared = function
	| ANewExpression(expr) -> absolute_newExpression data declared expr
	| ACallExpression(expr) -> absolute_callExpression data declared expr

and absolute_newExpression data declared = function
	| AMemberExpression(expr) -> absolute_memberExpression data declared expr
	| ANewMemberExpression(expr) -> absolute_newExpression data declared expr

and absolute_callExpression data declared = function
	| ACallMemberArgs(expr, args) ->
		let data = absolute_memberExpression data declared expr in
		absolute_arguments data declared args
	| ACallExpressionArgs(expr, args) ->
		let data = absolute_callExpression data declared expr in
		absolute_arguments data declared args
	| ACallExpressionExpr(expr1, expr2) -> 
		let data = absolute_callExpression data declared expr1 in
		absolute_expression data declared expr2
	| ACallExpressionIdent(expr, ident) ->
		absolute_callExpression data declared expr

and absolute_memberExpression data declared = function
	| APrimaryExpr(expr) -> absolute_primaryExpression data declared expr
	| AFunctionExpr(funcExpr) -> absolute_functionExpression data declared funcExpr
	| AMemberBracketExpr(expr1, expr2) -> 
		let data = absolute_memberExpression data declared expr1 in
		absolute_expression data declared expr2
	| AMemberDotExpr(expr, ident) ->
		absolute_memberExpression data declared expr
	| AMemberNewExpr(expr, args) ->
		let data = absolute_memberExpression data declared expr in
		absolute_arguments data declared args

and absolute_functionDeclaration data declared (_, _, _, _, params, body) =
	absolute_functionContent data declared params body

and absolute_functionExpression data declared (_, _, params, body) = 
	absolute_functionContent data declared params body

and absolute_functionContent data declared params body =
	(*let declared = StrSet.union declared (let fullfill = fullfill_functionContent params body in fullfill.idents) in*)
	let data = List.fold_right (fun i data -> {
		abs_idents = (StrSet.add i data.abs_idents)
	}) params data in
	absolute_body data declared body

and absolute_body data declared body =
	absolute_list absolute_sourceElement data declared body

and absolute_arguments data declared args =
	absolute_list absolute_assignmentExpression data declared args

and absolute_expressionStatement data declared expr =
	absolute_expression data declared expr

and absolute_ifStatement data declared = function
	| AIfElse(expr, stmt1, stmt2) ->
		let data = absolute_expression data declared expr in
		let data = absolute_statement data declared stmt1 in
		absolute_statement data declared stmt2
	| AIf(expr, stmt) ->
		let data = absolute_expression data declared expr in
		absolute_statement data declared stmt

and absolute_iterationStatement data declared = function
	| ADoWhile(stmt, expr) ->
		let data = absolute_statement data declared stmt in
		absolute_expression data declared expr
	| AWhile(expr, stmt) ->
		let data = absolute_expression data declared expr in
		absolute_statement data declared stmt
	| AFor(expr1, expr2, expr3, stmt) ->
		let data = (match expr1 with
			| None -> data
			| Some expr1 -> absolute_expression data declared expr1) in
		let data = (match expr2 with
			| None -> data
			| Some expr2 -> absolute_expression data declared expr2) in
		let data = (match expr3 with
			| None -> data
			| Some expr3 -> absolute_expression data declared expr3) in
		absolute_statement data declared stmt
	| AForIn(lhs, expr, stmt) ->
		let data = absolute_leftHandSideExpression data declared lhs in
		let data = absolute_expression data declared expr in
		absolute_statement data declared stmt
	| AForVar(_, varDecls, expr1, expr2, stmt) ->
		let data = absolute_variableDeclarationList data declared varDecls in
		let data = (match expr1 with
			| None -> data
			| Some expr1 -> absolute_expression data declared expr1) in
		let data = (match expr2 with
			| None -> data
			| Some expr2 -> absolute_expression data declared expr2) in
		absolute_statement data declared stmt
	| AForVarIn(_, varDecl, expr, stmt) ->
		let data = absolute_variableDeclaration data declared varDecl in
		let data = absolute_expression data declared expr in
		absolute_statement data declared stmt
	
and absolute_returnStatement data declared = function
	| None -> data
	| Some expr -> absolute_expression data declared expr

and absolute_withStatement data declared (expr, stmt) = 
	let data = absolute_expression data declared expr in
	absolute_statement data StrSet.empty stmt	(* Important : with() {} makes as if everything was overriden *)

and absolute_labelledStatement data declared (_, stmt) =
	absolute_statement data declared stmt

and absolute_switchStatement data declared (expr, caseBlock) = 
	let data = absolute_expression data declared expr in
	absolute_caseBlock data declared caseBlock

and absolute_caseBlock data declared = function
	| ACasesNoDefault(caseClauses) -> 
		absolute_caseClauses data declared caseClauses
	| ACasesDefault(caseClauses1, defaultClause, caseClauses2) ->
		let data = absolute_caseClauses data declared caseClauses1 in
		let data = absolute_defaultClause data declared defaultClause in
		absolute_caseClauses data declared caseClauses2

and absolute_defaultClause data declared stmts =
	absolute_list absolute_statement data declared stmts

and absolute_caseClauses data declared clauses = 
	absolute_list absolute_caseClause data declared clauses;
	
and absolute_caseClause data declared (expr,stmts) =
	let data = absolute_expression data declared expr in
	absolute_list absolute_statement data declared stmts

and absolute_throwStatement data declared expr =
	absolute_expression data declared expr

and absolute_tryStatement data declared = function
	| ATryCatch(block, catch) ->
		let data = absolute_block data declared block in
		absolute_catch data declared catch
	| ATryFinally(block, finally) ->
		let data = absolute_block data declared block in
		absolute_finally data declared finally
	| ATryCatchFinally(block, catch, finally) ->
		let data = absolute_block data declared block in
		let data = absolute_catch data declared catch in
		absolute_finally data declared finally

and absolute_catch data declared (_,block) =
	absolute_block data declared block

and absolute_finally data declared block =
	absolute_block data declared block

and absolute_conditionalExpression data declared = function
	| ACondBinaryExpr(expr) -> absolute_binaryExpression data declared expr
	| ACondTernaryExpr(expr, assignExpr1, assignExpr2) ->
		let data = absolute_binaryExpression data declared expr in
		let data = absolute_assignmentExpression data declared assignExpr1 in
		absolute_assignmentExpression data declared assignExpr2

and absolute_binaryExpression data declared = function
	| AUnaryExpr(unaryExpression) -> absolute_unaryExpression data declared unaryExpression
	| ABinaryExpr(_, expr1, expr2) ->
		let data = absolute_binaryExpression data declared expr1 in
		absolute_binaryExpression data declared expr2

and absolute_unaryExpression data declared = function
	| APostfixExpr(expr) -> absolute_postfixExpression data declared expr
	| AOpUnaryExpr(_, expr) -> absolute_unaryExpression data declared expr

and absolute_postfixExpression data declared = function
	| ALeftHandSide(lhs) -> absolute_leftHandSideExpression data declared lhs
	| ALeftHandSideOp(lhs, _) -> absolute_leftHandSideExpression data declared lhs

and absolute_expression data declared expr =
	absolute_list absolute_assignmentExpression data declared expr

and absolute_primaryExpression data declared = function
	| AThis -> data
	| AIdentifier(identifier) -> if StrSet.mem identifier declared then data else {
			abs_idents = StrSet.add identifier data.abs_idents
		}
	| ALiteral(literal) -> data
	| AArrayLiteral(arrayLiteral) -> absolute_arrayLiteral data declared arrayLiteral
	| AObjectLiteral(objectLiteral) -> absolute_objectLiteral data declared objectLiteral
	| AExpression(expr) -> absolute_expression data declared expr

and absolute_arrayLiteral data declared = function
	| AElisions(elisions) -> data
	| AElements(elements) -> absolute_elements data declared elements
	| AElementsAndElisions(elements, _) -> absolute_elements data declared elements

and absolute_elements data declared elements =
	absolute_list absolute_element data declared elements

and absolute_element data declared (_, expr) =
	absolute_assignmentExpression data declared expr

and absolute_objectLiteral data declared objectLiteral =
	absolute_list absolute_propertyAssignment data declared objectLiteral

and absolute_propertyAssignment data declared = function
	| APropAssignment(_, assignExpr) -> 
		absolute_assignmentExpression data declared assignExpr
	| APropGet(_, functionBody) ->
		absolute_functionContent data declared [] functionBody
	| APropSet(_, idents, functionBody) ->
		absolute_functionContent data declared idents functionBody

